package com.spring.theory.bean.beanFactoryPostProcessor.postProcessor;

import com.spring.theory.bean.beanFactoryPostProcessor.model.Config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;

/**
 * ComponentScanPostProcessor
 * --模拟实现 @Component 注解扫描注册bean的过程
 *
 * @author H
 * @version 1.0
 * @date 2023-12-26 11:16
 */
@Slf4j
public class ComponentScanPostProcessor implements BeanDefinitionRegistryPostProcessor {

    /**
     * BeanDefinitionRegistryPostProcessor 可以在所有Bean定义加载完成之后，Bean实例化之前被调用，进行自定义修改（增删改）
     * BeanFactoryPostProcessor 在BeanFactory完成实例化之后对BeanFactory进行后置处理
     * context.refresh();时会回调此接口
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        try {
            /**
             * spring查找注解的工具，可查找派生类注解
             */
            ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
            if (componentScan != null) {
                // 读取类元数据信息
                CachingMetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();
                AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
                for (String s : componentScan.basePackages()) {
                    log.info("扫描包路径：{}", s);
                    //扫描指定包下的所有类，并获取其元数据信息 ；两个*代表子包也会查询
                    String path = "classpath*:" + s.replace(".", "/") + "/**/*.class";
                    log.info("扫描包拼接后路径：{}", path);
                    Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
                    for (Resource resource : resources) {
                        log.info("扫描文件：{}", resource);
                        // 获取每个文件路径下，类的原信息；包括类名、注解、参数等等
                        MetadataReader metadataReader = readerFactory.getMetadataReader(resource);
                        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
                        //检查包路径下的所有文件是否包含需要指定的注解（普通注解和派生注解）
                        if (annotationMetadata.hasAnnotation(Component.class.getName())
                                || annotationMetadata.hasMetaAnnotation(Component.class.getName())) {
                            // 生成bean产品时，需要在bean定义里指定它的类名；生成bean工厂类时不需要指定类名
                            BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(metadataReader.getClassMetadata().getClassName()).getBeanDefinition();
                            //根据bean定义里面的信息生成beanName
                            String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry);
                            //注册bean；补充到bean工厂里
                            registry.registerBeanDefinition(beanName, beanDefinition);
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("扫描失败", e);
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}
